package com.apeli.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
 * @author liming
 */
public class SpringApplicationContext {

    /**
     * clazz
     */
    private Class<?> clazz;

    /**
     * beanDefinitionMap
     */
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * 单例池
     */
    private Map<String, Object> singletonObjects = new HashMap<>();

    /**
     * 后置处理器集合
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public SpringApplicationContext(Class<?> clazz) {
        this.clazz = clazz;
        // 扫描
        scan(clazz);

        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            if (Objects.equals(entry.getValue().getScope(), Scope.Type.SINGLETON)) {
                try {
                    // 如果为单例模式 将单例bean 提前放入单例池
                    singletonObjects.put(entry.getKey(), createBean(entry.getKey(), entry.getValue()));
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public Object getBean(String beanName) throws IllegalAccessException, InstantiationException {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Class<?> c = beanDefinition.getType();
        // 单例bean
        if (Objects.equals(Scope.Type.SINGLETON, beanDefinition.getScope())) {
            if (singletonObjects.containsKey(beanName)) {
                return singletonObjects.get(beanName);
            } else {
                Object o = c.newInstance();
                singletonObjects.put(beanName, o);
                return o;
            }
        } else {
            return createBean(beanName, beanDefinition);
        }
    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) throws IllegalAccessException, InstantiationException {
        Class<?> c = beanDefinition.getType();
        Object instance = c.newInstance();
        // 依赖注入
        for (Field field : c.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                field.set(instance, getBean(field.getName()));
            }
        }
        // BeanName ware回调接口
        if (instance  instanceof BeanNameAware) {
            ((BeanNameAware) instance).setBeanName(beanName);
        }

        // 初始化前后置处理器操作
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }

        // 初始化操作
        if (instance instanceof InitializingBean) {
            ((InitializingBean) instance).afterPropertiesSet();
        }

        // 初始化后后置处理器操作
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }

        return instance;
    }

    private void scan(Class<?> clazz) {
        String basePackage = "";
        ClassLoader loader = clazz.getClassLoader();
        // 确定需要扫描的包
        if (clazz.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = clazz.getAnnotation(ComponentScan.class);
            basePackage = componentScanAnnotation.value();
        }
        // 如果没有使用该注解或者注解没指定包，默认扫描配置类下面的类
        if (Objects.equals(basePackage, "")) {
            basePackage = clazz.getPackage().getName();
        }
        basePackage = basePackage.replace(".", "//");
        URL resource = loader.getResource(basePackage);
        File file = new File(resource.getFile());
        // 递归查询所有需要扫描的class文件
        List<String> classList = SpringUtils.findFiles(file, "class");
        for (String clazzString : classList) {
            // 得到需要加载类的权限类名
            String substring = clazzString.substring(clazzString.indexOf("com"), clazzString.indexOf(".class")).replace("\\", ".");
            try {
                Class<?> c = loader.loadClass(substring);
                String beanName = "";
                if (c.isAnnotationPresent(Component.class)) {
                    Component componentAnnotation = c.getAnnotation(Component.class);
                    beanName = componentAnnotation.value();
                    if (Objects.equals("", beanName)) {
                        beanName = Introspector.decapitalize(c.getSimpleName());
                    }
                    if (BeanPostProcessor.class.isAssignableFrom(c)) {
                        BeanPostProcessor instance = (BeanPostProcessor) c.newInstance();
                        beanPostProcessorList.add(instance);
                    }
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setType(c);
                    if (c.isAnnotationPresent(Scope.class)) {
                        Scope scopeAnnotation = c.getAnnotation(Scope.class);
                        Scope.Type type = scopeAnnotation.value();
                        beanDefinition.setScope(type);
                    } else {
                        beanDefinition.setScope(Scope.Type.SINGLETON);
                    }
                    beanDefinitionMap.put(beanName, beanDefinition);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }
}
